package com.imooc.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.imooc.Interceptor.JwtCurrentUserInterceptor;
import com.imooc.bo.NewArticleBO;
import com.imooc.enums.ArticleStatus;
import com.imooc.exception.GraceException;
import com.imooc.mapper.ArticleMapper;
import com.imooc.mq.DelayConfig_Article;
import com.imooc.pojo.Admin;
import com.imooc.pojo.Article;
import com.imooc.result.ResponseStatusEnum;
import com.imooc.service.ArticleService;
import com.imooc.utils.PageUtils;
import com.imooc.utils.PagedGridResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 文章表 服务实现类
 * </p>
 *
 * @author liansen
 * @since 2023-05-15
 */
@Slf4j
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Override
    public void createArticle(NewArticleBO newArticleBO) {

        Admin admin = JwtCurrentUserInterceptor.adminUser.get();

        //同一个用户不能发相同的文章
        String adminId = admin.getId();
        String title = newArticleBO.getTitle();

        LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Article::getTitle, title).eq(Article::getPublishAdminId, adminId);

        Long count = baseMapper.selectCount(wrapper);
        if(count > 0){
            GraceException.display(ResponseStatusEnum.ARTICLE_ALREADY_EXITS);
        }


        Article article = new Article();
        BeanUtils.copyProperties(newArticleBO, article);
        article.setCreateTime(LocalDateTime.now());
        article.setUpdateTime(LocalDateTime.now());


        article.setPublishAdminId(admin.getId());
        article.setPublisher(admin.getUsername());
        article.setPublisherFace(admin.getFace());


        //发布时间
        LocalDateTime publishTime = newArticleBO.getPublishTime();

        if(null != publishTime){
            article.setPublishTime(publishTime);
            article.setStatus(ArticleStatus.CLOSE.type);
            //延迟发布

        }else {
            article.setPublishTime(LocalDateTime.now());
            article.setStatus(ArticleStatus.OPEN.type);
        }
        baseMapper.insert(article);

        if(publishTime != null){
            //定时延迟发布
            LocalDateTime futureDate = newArticleBO.getPublishTime();
            LocalDateTime nowDate = LocalDateTime.now();
            //计算预发布时间和当前时间的时间差(毫秒)
//            long delayTimes = LocalDateUtils.getChronoUnitBetween(nowDate, futureDate, ChronoUnit.MILLIS, true);

            //10s延迟时间
            int delayTimes = 20 * 1000;

            //不管消息能不能成功到达交换器，都会触发这个回调方法
            rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
                /**
                 * @param correlationData 当前消息的唯一关联数据(消息的唯一id)
                 * @param ack 交换机是否成功接收到消息
                 * @param cause 失败的原因
                 */
                @Override
                public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                    log.info("correlationData消息的唯一id,{}", correlationData);
                    if(ack){
                        //成功的话, cause为null;
                        log.info("交换机成功接收到消息{}", cause);
                    }else {
                        log.info("交换机消息失败,失败原因是,{}", cause);
                    }
                }
            });

            //消息从交换机到队列，队列接收失败的话就会执行的回调方法
            rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
                @Override
                public void returnedMessage(ReturnedMessage returned) {
                    log.info("队列接收消息失败, 进入returnsCallback回调方法");
                    log.info("exchange:" + returned.getExchange());
                    log.info("replyText:" + returned.getReplyText());
                    log.info("routingKey:" + returned.getRoutingKey());
                    log.info("ReplyCode:" + returned.getReplyCode());
                }
            });

            //发布
            rabbitTemplate.convertAndSend(DelayConfig_Article.EXCHANGE_DELAY_ARTICLE,
                                            DelayConfig_Article.DELAY_BINDING_ARTICLE,
                                            article.getId(),
                                            DelayConfig_Article.setDelayTimes(delayTimes));

            System.out.println("延迟消息定时发布");
        }
    }


    @Override
    public void updateStatus(String articleId, Integer status) {
        Article article = new Article();
        article.setId(articleId);
        article.setStatus(status);
        article.setUpdateTime(LocalDateTime.now());
        baseMapper.updateById(article);
    }

    @Override
    public PagedGridResult queryList(ArticleStatus status, Integer page, Integer limit) {

        //开启分页
        PageHelper.startPage(page, limit);

        LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();

        if(status != null ){
            wrapper.eq(Article::getStatus, status);
        }

        wrapper.orderByDesc(Article::getCreateTime);

        List<Article> list = baseMapper.selectList(wrapper);

        return PageUtils.setterPagedGrid(list, page);

    }

    @Override
    public Article detail(String id) {
        LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Article::getId, id);
        return baseMapper.selectOne(wrapper);
    }

    @Override
    public void updateArticle(NewArticleBO newArticleBO) {
        Article article = new Article();
        BeanUtils.copyProperties(newArticleBO, article);
        article.setId(newArticleBO.getArticleId());
        article.setUpdateTime(LocalDateTime.now());
        baseMapper.updateById(article);
    }
}
